import os
import subprocess
import pytest
from typing import List, Tuple

# Assume the executable and test data are in specific directories.
# You should change these paths to match your project structure.
# For example, the executable might be in the root directory, and test data in a 'data' folder.
# The paths are relative to the root of the project where you run pytest.
EXE_PATH = os.path.join("code.exe")
TEST_DATA_DIR = os.path.join("test_data")

def find_test_cases() -> List[Tuple[str, str]]:
    """
    Finds all test cases in the TEST_DATA_DIR.
    A test case is a pair of (.in, .out) files with the same base name.
    
    Returns:
        A list of tuples, where each tuple contains the full path to an
        input file and its corresponding output file.
    """
    if not os.path.isdir(TEST_DATA_DIR):
        # Return an empty list if the directory doesn't exist,
        # pytest will mark the test as skipped.
        return []

    test_cases = []
    for filename in os.listdir(TEST_DATA_DIR):
        if filename.endswith(".in"):
            base_name = os.path.splitext(filename)[0]
            in_file = os.path.join(TEST_DATA_DIR, filename)
            out_file = os.path.join(TEST_DATA_DIR, base_name + ".out")
            if os.path.exists(out_file):
                test_cases.append((in_file, out_file))
    return test_cases

def run_executable(exe_path: str, input_data: str) -> str:
    """
    Runs the specified executable with the given input data.

    Args:
        exe_path: The path to the executable file.
        input_data: The string data to be passed to the executable's standard input.

    Returns:
        The standard output from the executable as a string.
    """
    process = subprocess.run(
        [exe_path],
        input=input_data,
        capture_output=True,
        text=True,
        encoding='utf-8',
        timeout=5  # 5-second timeout to prevent hanging
    )
    return process.stdout

# Use pytest.mark.parametrize to create a test for each found test case
@pytest.mark.parametrize("in_file, out_file", find_test_cases())
def test_executable_with_file(in_file: str, out_file: str):
    """
    Tests the executable against a single input/output file pair.
    """
    if not os.path.exists(EXE_PATH):
        pytest.fail(f"Executable not found at: {EXE_PATH}")

    with open(in_file, 'r', encoding='utf-8') as f:
        input_data = f.read()

    with open(out_file, 'r', encoding='utf-8') as f:
        expected_output = f.read()

    actual_output = run_executable(EXE_PATH, input_data)

    # Normalize newlines and strip trailing whitespace for a robust comparison
    normalized_actual = actual_output.strip().replace('\r\n', '\n')
    normalized_expected = expected_output.strip().replace('\r\n', '\n')

    assert normalized_actual == normalized_expected, \
        f"Output mismatch for test case {os.path.basename(in_file)}"

# A fallback test that runs if no test cases were found
def test_check_for_test_cases():
    """
    Checks if any test cases were found. If not, skips the test with a message.
    """
    if not find_test_cases():
        pytest.skip(f"No test cases (.in/.out pairs) found in '{TEST_DATA_DIR}'")

if __name__ == "__main__":
    test_check_for_test_cases()
    test_cases = find_test_cases()
    for in_f, out_f in test_cases:
        test_executable_with_file(in_f, out_f)